////
#include "fs.hpp"
#include "finaly.hpp"
#include <dirent.h>
#include <unistd.h>

namespace gitee {
using stat_t = struct stat;
using dirent_t = struct dirent;
std::string BaseName(gitee::string_view sv) {
  if (sv.empty()) {
    return ".";
  }
  auto pos = sv.find_last_not_of('/');
  if (pos == gitee::string_view::npos) {
    return "/";
  }
  sv.remove_suffix(sv.size() - pos - 1);
  pos = sv.rfind('/');
  if (pos != gitee::string_view::npos) {
    sv.remove_prefix(pos + 1);
  }
  if (sv.empty()) {
    return ".";
  }
  return std::string(sv.data(), sv.size());
}

std::string DirName(gitee::string_view sv) {
  if (sv.empty()) {
    return ".";
  }
  size_t index = sv.size() - 1;
  for (; sv[index] == '/'; index--) {
    if (index == 0) {
      return "/";
    }
  }
  for (; sv[index] != '/'; index--) {
    if (index == 0) {
      return ".";
    }
  }
  for (; sv[index] == '/'; index--) {
    if (index == 0) {
      return "/";
    }
  }
  return std::string(sv.data(), index + 1);
}

std::vector<gitee::string_view> SplitPath(gitee::string_view sv) {
  std::vector<gitee::string_view> pv;
  size_t first = 0;
  while (first < sv.size()) {
    const auto second = sv.find_first_of('/', first);
    if (first != second) {
      auto s = sv.substr(first, second - first);
      if (s == "..") {
        if (pv.empty()) {
          break;
        }
        pv.pop_back();
      } else if (s != ".") {
        pv.emplace_back(s);
      }
    }
    if (second == gitee::string_view::npos) {
      break;
    }
    first = second + 1;
  }
  return pv;
}

inline bool MakeDir(gitee::string_view dir, mode_t mode) {
  return (mkdir(dir.data(), mode) == 0 || errno == EEXIST);
}

bool MakeDirRecurseFullPath(gitee::string_view dir, mode_t mode) {
  auto pv = SplitPath(dir);
  if (pv.empty()) {
    return false;
  }
  std::string dirbuf;
  dirbuf.reserve(dir.size());
  for (auto s : pv) {
    dirbuf.append("/").append(s.data(), s.size());
    if (!MakeDir(dirbuf, mode)) {
      return false;
    }
  }
  return true;
}

bool RemoveDirInternal(gitee::string_view path) {
  DIR *d = opendir(path.data());
  if (d == nullptr) {
    return false;
  }
  auto deleter = bela::finally([&] {
    //
    closedir(d);
  });
  gitee::DirBuffer db(path);
  dirent_t *dt = nullptr;
  while ((dt = readdir(d)) != nullptr) {
    if (DirSkipFaster(dt->d_name)) {
      continue;
    }
    db.Append(dt->d_name);
    if (dt->d_type == DT_DIR) {
      if (!RemoveDirInternal(db.sv())) {
        return false;
      }
      if (rmdir(db.data()) != 0) {
        return false;
      }
      continue;
    }
    if (unlink(db.data()) != 0) {
      return false;
    }
  }
  return true;
}

bool RecurseRmdir(gitee::string_view dir) {
  if (!RemoveDirInternal(dir)) {
    return false;
  }
  return rmdir(dir.data()) == 0;
}

} // namespace gitee
